#include <stdint.h>

#include "interface.h"
#include "reg.h"
#include "pcd_def.h"

#include "pcd.h"

void pcd_reset(void)
{
	// 需先保持高电平，后给个下降沿
	rc522_set_rst(0);
	delay_ms(5);
	rc522_set_rst(1);
	delay_ms(10);

	write_reg(CommandReg, PCD_RESETPHASE);
	delay_ms(1);

	// 和MI卡通讯，CRC初始值0x6363
	write_reg(ModeReg, 0x3D);
	write_reg(TReloadRegL, 30);
	write_reg(TReloadRegH, 0);
	write_reg(TModeReg, 0x8D);
	write_reg(TPrescalerReg, 0x3E);
	write_reg(TxASKReg, 0x40);
}

void pcd_antenna_on(void)
{
	uint8_t t;
	t = read_reg(TxControlReg);

	if (!(t & 0x03)) {
		set_bit_mask(TxControlReg, 0x03);
	}
}

void pcd_antenna_off(void)
{
	// 每次启动或关闭天线发射之间至少有1ms的间隔
	delay_ms(1);

	clear_bit_mask(TxControlReg, 0x03);
}

pcd_err_t pcd_com_with_picc(uint8_t command, uint8_t *send_data,
			    uint8_t send_data_len_byte, uint8_t *recv_data,
			    uint32_t *recv_data_len_bit)
{
	pcd_err_t status = PCD_ERR;
	uint8_t irqEn = 0x00;
	uint8_t waitFor = 0x00;
	uint8_t lastBits;
	uint8_t n;
	uint32_t i;
	uint8_t j;

	switch (command) {
	case PCD_AUTHENT:
		irqEn = 0x12;
		waitFor = 0x10;
		break;
	case PCD_TRANSCEIVE:
		irqEn = 0x77;
		waitFor = 0x30;
		break;
	default:
		break;
	}

	write_reg(ComIEnReg, irqEn | 0x80);
	clear_bit_mask(ComIrqReg, 0x80);
	write_reg(CommandReg, PCD_IDLE);

	// 清空FIFO
	set_bit_mask(FIFOLevelReg, 0x80);

	for (i = 0; i < send_data_len_byte; i++) {
		// 数据写入FIFO
		write_reg(FIFODataReg, send_data[i]);
	}

	// 命令写入命令寄存器
	write_reg(CommandReg, command);

	if (command == PCD_TRANSCEIVE) {
		// 开始发送
		set_bit_mask(BitFramingReg, 0x80);
	}

	// 根据时钟频率调整，操作M1卡最大等待时间25ms 2000?
	i = 25;
	do {
		n = read_reg(ComIrqReg);
		i--;
		delay_ms(1);
	} while ((i != 0) && !(n & 0x01) && !(n & waitFor));
	clear_bit_mask(BitFramingReg, 0x80);

	if (i != 0) {
		j = read_reg(ErrorReg);
		if (!(j & 0x1B)) {
			status = PCD_OK;
			if (n & irqEn & 0x01) {
				status = PCD_NOTAGERR;
			}
			if (command == PCD_TRANSCEIVE) {
				n = read_reg(FIFOLevelReg);
				lastBits = read_reg(ControlReg) & 0x07;
				if (lastBits) {
					*recv_data_len_bit =
						(n - 1) * 8 + lastBits;
				} else {
					*recv_data_len_bit = n * 8;
				}
				if (n == 0) {
					n = 1;
				}
				if (n > MAXRLEN) {
					n = MAXRLEN;
				}
				for (i = 0; i < n; i++) {
					recv_data[i] = read_reg(FIFODataReg);
				}
			}
		} else {
			status = PCD_ERR;
		}
	}

	// stop timer now
	set_bit_mask(ControlReg, 0x80);
	write_reg(CommandReg, PCD_IDLE);

	return status;
}

pcd_err_t pcd_request(uint8_t req_code, uint8_t *tag_type)
{
	pcd_err_t status;
	uint32_t len;
	uint8_t com_buf[MAXRLEN];

	clear_bit_mask(Status2Reg, 0x08);
	write_reg(BitFramingReg, 0x07);
	set_bit_mask(TxControlReg, 0x03);

	com_buf[0] = req_code;

	// 发送并接收数据
	status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 1, com_buf, &len);
	if ((status == PCD_OK) && (len == 0x10)) {
		// ESP_LOGI(TAG, "mi_ok");
		*tag_type = com_buf[0];
		*(tag_type + 1) = com_buf[1];
	} else {
		// ESP_LOGI(TAG, "mi_err");
		status = PCD_ERR;
	}

	return status;
}

pcd_err_t pcd_anticoll(uint8_t *serial_num)
{
	pcd_err_t status;
	uint8_t i, serial_num_check = 0;
	uint32_t len;
	uint8_t com_buf[MAXRLEN];

	// 寄存器包含接收器和发送器和数据模式检测器的状态标志
	clear_bit_mask(Status2Reg, 0x08);

	// 不启动数据发送，接收的LSB位存放在位0，接收到的第二位放在位1，定义发送的最后一个字节位数为8
	write_reg(BitFramingReg, 0x00);

	// 所有接收的位在冲突后将被清除
	clear_bit_mask(CollReg, 0x80);

	com_buf[0] = PICC_ANTICOLL1;
	com_buf[1] = 0x20;

	status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 2, com_buf, &len);

	if (status == PCD_OK) {
		for (i = 0; i < 4; i++) {
			*(serial_num + i) = com_buf[i];
			serial_num_check ^= com_buf[i];
		}
		// 返回四个字节，最后一个字节为校验位
		if (serial_num_check != com_buf[i]) {
			status = PCD_ERR;
		}
	}

	set_bit_mask(CollReg, 0x80);

	return status;
}

void pcd_calc_crc(const uint8_t *input, uint8_t len, uint8_t *output)
{
	uint8_t i, n;

	clear_bit_mask(DivIrqReg, 0x04);
	write_reg(CommandReg, PCD_IDLE);
	set_bit_mask(FIFOLevelReg, 0x80);

	for (i = 0; i < len; i++) {
		write_reg(FIFODataReg, *(input + i));
	}

	write_reg(CommandReg, PCD_CALCCRC);

	i = 0xFF;

	do {
		n = read_reg(DivIrqReg);
		i--;
	} while ((i != 0) && !(n & 0x04));

	output[0] = read_reg(CRCResultRegL);
	output[1] = read_reg(CRCResultRegM);
}

pcd_err_t pcd_select(const uint8_t *serial_num)
{
	pcd_err_t status;
	uint8_t i;
	uint8_t com_buf[MAXRLEN];
	uint32_t len;

	com_buf[0] = PICC_ANTICOLL1;
	com_buf[1] = 0x70;
	com_buf[6] = 0;

	for (i = 0; i < 4; i++) {
		com_buf[i + 2] = *(serial_num + i);
		com_buf[6] ^= *(serial_num + i);
	}

	pcd_calc_crc(com_buf, 7, &com_buf[7]);

	clear_bit_mask(Status2Reg, 0x08);

	status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 9, com_buf, &len);

	if ((status == PCD_OK) && (len == 0x18)) {
		status = PCD_OK;
	} else {
		status = PCD_ERR;
	}

	return status;
}

pcd_err_t pcd_auth(uint8_t auth_mode, uint8_t addr, const uint8_t *key,
		   const uint8_t *serial_num)
{
	pcd_err_t status;
	uint8_t i, com_buf[MAXRLEN];
	uint32_t len;

	com_buf[0] = auth_mode;
	com_buf[1] = addr;

	for (i = 0; i < 6; i++) {
		com_buf[i + 2] = *(key + i);
	}

	for (i = 0; i < 6; i++) {
		com_buf[i + 8] = *(serial_num + i);
	}

	status = pcd_com_with_picc(PCD_AUTHENT, com_buf, 12, com_buf, &len);

	if ((status != PCD_OK) || (!(read_reg(Status2Reg) & 0x08))) {
		status = PCD_ERR;
	}

	return status;
}

pcd_err_t pcd_read(uint8_t addr, uint8_t data[16])
{
	pcd_err_t status;
	uint8_t i, com_buf[MAXRLEN];
	uint32_t len;

	com_buf[0] = PICC_READ;
	com_buf[1] = addr;

	pcd_calc_crc(com_buf, 2, &com_buf[2]);

	status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 4, com_buf, &len);

	if ((status == PCD_OK) && (len == 0x90)) {
		for (i = 0; i < 16; i++) {
			*(data + i) = com_buf[i];
		}
	} else {
		status = PCD_ERR;
	}

	return status;
}

pcd_err_t pcd_write(uint8_t addr, const uint8_t data[16])
{
	pcd_err_t status;
	uint8_t i, com_buf[MAXRLEN];
	uint32_t len;

	com_buf[0] = PICC_WRITE;
	com_buf[1] = addr;

	pcd_calc_crc(com_buf, 2, &com_buf[2]);

	status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 4, com_buf, &len);
	if ((status != PCD_OK) || (len != 4) || ((com_buf[0] & 0x0F) != 0x0A)) {
		status = PCD_ERR;
	}

	if (status == PCD_OK) {
		for (i = 0; i < 16; i++) {
			com_buf[i] = *(data + i);
		}
		pcd_calc_crc(com_buf, 16, &com_buf[16]);

		status = pcd_com_with_picc(PCD_TRANSCEIVE, com_buf, 18, com_buf,
					   &len);
		if ((status != PCD_OK) || (len != 4) ||
		    ((com_buf[0] & 0x0F) != 0x0A)) {
			status = PCD_ERR;
		}
	}

	return status;
}